package cn.mulanbay.business.config;

import cn.mulanbay.business.handler.CacheHandler;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

/**
 * 缓存配置
 *
 * @author fenghong
 * @create 2022-01-16 10:00
 */
public class CacheConfig {

    private static final Logger logger = LoggerFactory.getLogger(CacheConfig.class);

    /**
     * 密码
     */
    @Value("${redis.cluster.password}")
    String password;

    /**
     * 集群节点
     */
    @Value("${redis.cluster.nodes}")
    String clusterNodes;

    /**
     * 在群集中执行命令时要遵循的最大重定向数目
     */
    @Value("${redis.cluster.maxRedirects:5}")
    int maxRedirects;

    /**
     * #以毫秒为单位的连接超时时间
     */
    @Value("${redis.pool.readTimeout:2000}")
    int readTimeout;

    /**
     * Redis连接池在给定时间可以分配的最大连接数。使用负值无限制
     */
    @Value("${redis.pool.maxTotal:300}")
    int maxTotal;

    /**
     * 池中“空闲”连接的最大数量。使用负值表示无限数量的空闲连接
     */
    @Value("${redis.pool.maxIdle:8}")
    int maxIdle;

    /**
     * 目标为保持在池中的最小空闲连接数。这个设置只有在设置max-idle的情况下才有效果
     */
    @Value("${redis.pool.minIdle:5}")
    int minIdle;

    /**
     * 连接分配在池被耗尽时抛出异常之前应该阻塞的最长时间量（以毫秒为单位）。使用负值可以无限期地阻止
     */
    @Value("${redis.pool.maxWait:3000}")
    int maxWait;

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        logger.debug("初始化cacheManager");
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //配置该objectMapper在反序列化时，忽略目标对象没有的属性。凡是使用该objectMapper反序列化时，都会拥有该特性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // 配置序列化
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //设置默认超过期时间是30秒
        config.entryTtl(Duration.ofSeconds(30));
        RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
        RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(redisCacheConfiguration).build();
        return cacheManager;


    }

    /**
     * redis
     *
     * @return
     */
    @Primary
    @Bean
    public RedisHttpSessionConfiguration redisHttpSessionConfiguration() {
        RedisHttpSessionConfiguration rhc = new RedisHttpSessionConfiguration();
        rhc.setMaxInactiveIntervalInSeconds(600);
        return rhc;
    }

    /**
     * redis连接池
     * @return
     */
    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        /**
         * 集群配置
         */
        String[] serverArray = clusterNodes.split(",");
        List<String> nodes = new ArrayList<>();
        for(String sv : serverArray){
            nodes.add(sv);
        }
        if(nodes.size()>1){
            logger.info("创建Redis集群版本链接");
            return this.createClusterConnection(nodes);
        }else{
            logger.info("创建Redis单机版本链接");
            return this.createStandaloneConnection(nodes.get(0));
        }
    }

    /**
     * 单机模式
     * @param node
     * @return
     */
    private JedisConnectionFactory createStandaloneConnection(String node){
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        String[] ss = node.split(":");
        config.setHostName(ss[0]);
        config.setPort(Integer.valueOf(ss[1]));
        config.setPassword(password);
        config.setDatabase(0);
        JedisConnectionFactory jcf = new JedisConnectionFactory(config);
        return jcf;
    }

    /**
     * 集群
     * @param nodes
     * @return
     */
    private JedisConnectionFactory createClusterConnection(List<String> nodes){
        RedisClusterConfiguration config = new RedisClusterConfiguration(nodes);
        config.setPassword(password);
        config.setMaxRedirects(maxRedirects);
        /**
         * 连接池配置
         */
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(maxTotal);
        poolConfig.setMaxIdle(maxIdle);
        poolConfig.setMinIdle(minIdle);
        poolConfig.setMaxWaitMillis(maxWait);
        JedisClientConfiguration clientConfig = JedisClientConfiguration.builder()
                .usePooling()
                .poolConfig(poolConfig)
                .and()
                .readTimeout(Duration.ofMillis(readTimeout))
                .build();
        JedisConnectionFactory jcf = new JedisConnectionFactory(config,clientConfig);
        return jcf;
    }

    /**
     * 缓存的key序列化方案
     *
     * @return
     */
    @Bean
    public StringRedisSerializer stringRedisSerializer() {
        return new StringRedisSerializer();
    }

    /**
     * redis template
     * @param jedisConnectionFactory
     * @param stringRedisSerializer
     * @return
     */
    @Bean
    public RedisTemplate redisTemplate(JedisConnectionFactory jedisConnectionFactory, StringRedisSerializer stringRedisSerializer) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        return redisTemplate;
    }

    /**
     * 缓存处理
     * @return
     */
    @Bean
    public CacheHandler cacheHandler(){
        CacheHandler cacheHandler = new CacheHandler();
        return cacheHandler;
    }
}
